Skip to content

fix(types): propagate destination generic to ConversionResult return#95

Open
ryanyue123 wants to merge 1 commit into
harshankur:masterfrom
ryanyue123:fix/conversion-result-generic-propagation
Open

fix(types): propagate destination generic to ConversionResult return#95
ryanyue123 wants to merge 1 commit into
harshankur:masterfrom
ryanyue123:fix/conversion-result-generic-propagation

Conversation

@ryanyue123
Copy link
Copy Markdown

Summary

OfficeParserAST.to() and OfficeConverter.convert() both declare their return as Promise<ConversionResult> (so D defaults to the full UniversalGeneratorFormat union) instead of Promise<ConversionResult<D>>. The result: callers passing a literal destination like "md" or "pdf" can't narrow .value from the full union (string | Uint8Array | OfficeChunk[]) to the specific type the runtime conditional already guarantees.

This PR threads the destination generic into the return type so the conditional in ConversionResult.value actually flows from the destination argument.

Changes

  • types.ts: OfficeParserAST.to<D>(...): Promise<ConversionResult>Promise<ConversionResult<D>>.
  • utils/astUtils.ts: matching implementation return type.
  • OfficeConverter.ts: add an explicit D extends SupportedDestination<T> generic so the destination literal carries through to ConversionResult<D> and OfficeConverterConfig<D, T>.

No runtime behavior change — the conditional in ConversionResult.value already produced the right runtime type per destination; this just lets TypeScript see it.

Verification

import { OfficeConverter, parseOffice } from "officeparser";

const buf = new Uint8Array(1);

// Before this PR: r1.value is `string | Uint8Array | OfficeChunk[]`
// After:          r1.value is `string`
const r1 = await OfficeConverter.convert(buf, "md");
const md: string = r1.value;

const ast = await parseOffice(buf);
const r2 = await ast.to("md");
const md2: string = r2.value;

const r3 = await ast.to("pdf");
const pdf: Uint8Array = r3.value;

const r4 = await ast.to("chunks");
const chunks: OfficeChunk[] = r4.value;

Build (npm run build:node) passes locally.

Motivation

Hit this in a production worker where we extract markdown from uploaded proposals. The workaround we ship today is a runtime typeof value !== "string" guard purely to satisfy the type system; the guard can never fire at runtime because the library's own conditional type guarantees string for "md". Once this PR ships we drop that guard and let TypeScript reflect the runtime contract.

Both OfficeParserAST.to() and OfficeConverter.convert() declared their
return as Promise<ConversionResult> (defaulting D to the full
UniversalGeneratorFormat union) instead of Promise<ConversionResult<D>>.
Callers passing a literal destination like "md" or "pdf" couldn't narrow
.value from the union (string | Uint8Array | OfficeChunk[]) to the
specific type the runtime conditional guarantees.

- OfficeParserAST.to (types.ts + utils/astUtils.ts impl):
  Promise<ConversionResult> → Promise<ConversionResult<D>>.

- OfficeConverter.convert: add an explicit D generic (defaults to
  SupportedDestination<T>) so destination's literal flows into the
  ConversionResult and OfficeConverterConfig type args.

Now:
  await OfficeConverter.convert(buf, "md")    → value: string
  await OfficeConverter.convert(buf, "pdf")   → value: Uint8Array
  await ast.to("chunks")                       → value: OfficeChunk[]

No runtime change; existing call sites that read value directly were
already relying on this narrowing implicitly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant